+2007-06-04 Michael Natterer <mitch@imendio.com>
+
+ Move "move-focus" signals from several widgets to GtkWidget to
+ enable more flexible costomization of keyboard navigation via
+ bindings. Fixes bug #414947.
+
+ * gtk/gtkwidget.c: add "move-focus" binding signal, default to
+ calling the toplevel GtkWindow's "move-focus" vfunc.
+
+ * gtk/gtktextview.[ch]
+ * gtk/gtkwindow.[ch]: remove "move-focus" signals and add compat
+ code that makes sure that both emitting the signal on the widget
+ and overriding the virtual functions keeps working as before.
+
+ * gtk/gtktoolbar.c: remove "move-focus" signal here too and use
+ GtkWidget's signal. This change slightly changes keyboard
+ navigation in toolbars. I'll fix the behavior if somebody can
+ explain me if and how exactly the new behavior is broken.
+
2007-06-04 Matthias Clasen <mclasen@redhat.com>
* gtk/gtkmarshalers.list:
COPY_CLIPBOARD,
PASTE_CLIPBOARD,
TOGGLE_OVERWRITE,
- MOVE_FOCUS,
MOVE_VIEWPORT,
SELECT_ALL,
TOGGLE_CURSOR_VISIBLE,
static void gtk_text_view_draw_focus (GtkWidget *widget);
static gboolean gtk_text_view_focus (GtkWidget *widget,
GtkDirectionType direction);
+static void gtk_text_view_move_focus (GtkWidget *widget,
+ GtkDirectionType direction_type);
static void gtk_text_view_select_all (GtkWidget *widget,
gboolean select);
static void gtk_text_view_paste_clipboard (GtkTextView *text_view);
static void gtk_text_view_toggle_overwrite (GtkTextView *text_view);
static void gtk_text_view_toggle_cursor_visible (GtkTextView *text_view);
-static void gtk_text_view_move_focus (GtkTextView *text_view,
+static void gtk_text_view_compat_move_focus(GtkTextView *text_view,
GtkDirectionType direction_type);
static void gtk_text_view_unselect (GtkTextView *text_view);
widget_class->motion_notify_event = gtk_text_view_motion_event;
widget_class->expose_event = gtk_text_view_expose_event;
widget_class->focus = gtk_text_view_focus;
-
+
+ /* need to override the base class function via override_class_closure,
+ * because the signal slot is not available in GtkWidgetCLass
+ */
+ g_signal_override_class_closure (g_signal_lookup ("move-focus",
+ GTK_TYPE_WIDGET),
+ GTK_TYPE_TEXT_VIEW,
+ g_cclosure_new (G_CALLBACK (gtk_text_view_move_focus),
+ NULL, NULL));
+
widget_class->drag_begin = gtk_text_view_drag_begin;
widget_class->drag_end = gtk_text_view_drag_end;
widget_class->drag_data_get = gtk_text_view_drag_data_get;
klass->copy_clipboard = gtk_text_view_copy_clipboard;
klass->paste_clipboard = gtk_text_view_paste_clipboard;
klass->toggle_overwrite = gtk_text_view_toggle_overwrite;
- klass->move_focus = gtk_text_view_move_focus;
+ klass->move_focus = gtk_text_view_compat_move_focus;
klass->set_scroll_adjustments = gtk_text_view_set_scroll_adjustments;
/*
_gtk_marshal_VOID__VOID,
G_TYPE_NONE, 0);
- signals[MOVE_FOCUS] =
- g_signal_new (I_("move_focus"),
- G_OBJECT_CLASS_TYPE (gobject_class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (GtkTextViewClass, move_focus),
- NULL, NULL,
- _gtk_marshal_VOID__ENUM,
- G_TYPE_NONE, 1,
- GTK_TYPE_DIRECTION_TYPE);
-
signals[SET_SCROLL_ADJUSTMENTS] =
g_signal_new (I_("set_scroll_adjustments"),
G_OBJECT_CLASS_TYPE (gobject_class),
obscure = TRUE;
}
else
- gtk_text_view_move_focus (text_view,
- (event->state & GDK_SHIFT_MASK) ?
- GTK_DIR_TAB_BACKWARD: GTK_DIR_TAB_FORWARD);
+ g_signal_emit_by_name (text_view, "move-focus",
+ (event->state & GDK_SHIFT_MASK) ?
+ GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD);
retval = TRUE;
}
}
}
+static void
+gtk_text_view_move_focus (GtkWidget *widget,
+ GtkDirectionType direction_type)
+{
+ GtkTextView *text_view = GTK_TEXT_VIEW (widget);
+
+ if (GTK_TEXT_VIEW_GET_CLASS (text_view)->move_focus)
+ GTK_TEXT_VIEW_GET_CLASS (text_view)->move_focus (text_view,
+ direction_type);
+}
+
/*
* Container
*/
if (!gtk_widget_keynav_failed (GTK_WIDGET (text_view),
leave_direction))
{
- gtk_text_view_move_focus (text_view, leave_direction);
+ g_signal_emit_by_name (text_view, "move-focus", leave_direction);
}
}
else
}
static void
-gtk_text_view_move_focus (GtkTextView *text_view,
- GtkDirectionType direction_type)
+gtk_text_view_compat_move_focus (GtkTextView *text_view,
+ GtkDirectionType direction_type)
{
- GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (text_view));
+ GSignalInvocationHint *hint = g_signal_get_invocation_hint (text_view);
- if (!GTK_WIDGET_TOPLEVEL (toplevel))
- return;
+ /* as of GTK+ 2.12, the "move-focus" signal has been moved to GtkWidget,
+ * the evil code below makes sure that both emitting the signal and
+ * calling the virtual function directly continue to work as expetcted
+ */
+
+ if (hint->signal_id == g_signal_lookup ("move-focus", GTK_TYPE_WIDGET))
+ {
+ /* if this is a signal emission, chain up */
+
+ GValue instance_and_params[2] = { { 0, }, { 0, } };
+ GValue return_value = { 0, };
+
+ g_value_init (&instance_and_params[0], GTK_TYPE_WIDGET);
+ g_value_set_object (&instance_and_params[0], text_view);
+
+ g_value_init (&instance_and_params[1], GTK_TYPE_DIRECTION_TYPE);
+ g_value_set_enum (&instance_and_params[1], direction_type);
- /* Propagate to toplevel */
- g_signal_emit_by_name (toplevel, "move_focus", direction_type);
+ g_signal_chain_from_overridden (instance_and_params, &return_value);
+
+ g_value_unset (&instance_and_params[0]);
+ g_value_unset (&instance_and_params[1]);
+ g_value_unset (&return_value);
+ }
+ else
+ {
+ /* otherwise emit the signal, since somebody called the virtual
+ * function directly
+ */
+
+ g_signal_emit_by_name (text_view, "move-focus", direction_type);
+ }
}
/*
/* overwrite */
void (* toggle_overwrite) (GtkTextView *text_view);
- /* propagates to GtkWindow move_focus */
+ /* as of GTK+ 2.12 the "move-focus" signal has been moved to GtkWidget,
+ * so this is merley a virtual function now. Overriding it in subclasses
+ * continues to work though.
+ */
void (* move_focus) (GtkTextView *text_view,
- GtkDirectionType direction);
-
-
+ GtkDirectionType direction);
+
/* Padding for future expansion */
void (*_gtk_reserved1) (void);
void (*_gtk_reserved2) (void);
ORIENTATION_CHANGED,
STYLE_CHANGED,
POPUP_CONTEXT_MENU,
- MOVE_FOCUS,
FOCUS_HOME_OR_END,
LAST_SIGNAL
};
GtkStyle *prev_style);
static gboolean gtk_toolbar_focus (GtkWidget *widget,
GtkDirectionType dir);
+static void gtk_toolbar_move_focus (GtkWidget *widget,
+ GtkDirectionType dir);
static void gtk_toolbar_screen_changed (GtkWidget *widget,
GdkScreen *previous_screen);
static void gtk_toolbar_map (GtkWidget *widget);
GtkOrientation orientation);
static void gtk_toolbar_real_style_changed (GtkToolbar *toolbar,
GtkToolbarStyle style);
-static gboolean gtk_toolbar_move_focus (GtkToolbar *toolbar,
- GtkDirectionType dir);
static gboolean gtk_toolbar_focus_home_or_end (GtkToolbar *toolbar,
gboolean focus_home);
static gboolean gtk_toolbar_button_press (GtkWidget *toolbar,
widget_class->size_allocate = gtk_toolbar_size_allocate;
widget_class->style_set = gtk_toolbar_style_set;
widget_class->focus = gtk_toolbar_focus;
+
+ /* need to override the base class function via override_class_closure,
+ * because the signal slot is not available in GtkWidgetClass
+ */
+ g_signal_override_class_closure (g_signal_lookup ("move_focus",
+ GTK_TYPE_WIDGET),
+ GTK_TYPE_TOOLBAR,
+ g_cclosure_new (G_CALLBACK (gtk_toolbar_move_focus),
+ NULL, NULL));
+
widget_class->screen_changed = gtk_toolbar_screen_changed;
widget_class->realize = gtk_toolbar_realize;
widget_class->unrealize = gtk_toolbar_unrealize;
G_TYPE_BOOLEAN, 3,
G_TYPE_INT, G_TYPE_INT,
G_TYPE_INT);
- /**
- * GtkToolbar::move-focus:
- * @toolbar: the #GtkToolbar which emitted the signal
- * @dir: a #GtkDirection
- *
- * A keybinding signal used internally by GTK+. This signal can't
- * be used in application code.
- *
- * Return value: %TRUE if the signal was handled, %FALSE if not
- */
- toolbar_signals[MOVE_FOCUS] =
- _gtk_binding_signal_new (I_("move_focus"),
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_CALLBACK (gtk_toolbar_move_focus),
- NULL, NULL,
- _gtk_marshal_BOOLEAN__ENUM,
- G_TYPE_BOOLEAN, 1,
- GTK_TYPE_DIRECTION_TYPE);
+
/**
* GtkToolbar::focus-home-or-end:
* @toolbar: the #GtkToolbar which emitted the signal
/* Keybinding handler. This function is called when the user presses
* Ctrl TAB or an arrow key.
*/
-static gboolean
-gtk_toolbar_move_focus (GtkToolbar *toolbar,
+static void
+gtk_toolbar_move_focus (GtkWidget *widget,
GtkDirectionType dir)
{
+ GtkToolbar *toolbar = GTK_TOOLBAR (widget);
+ GtkContainer *container = GTK_CONTAINER (toolbar);
GList *list;
gboolean try_focus = FALSE;
GList *children;
- GtkContainer *container = GTK_CONTAINER (toolbar);
-
+
+ g_printerr ("%s (dir = %d)\n", G_STRFUNC, dir);
+
if (container->focus_child &&
gtk_widget_child_focus (container->focus_child, dir))
{
- return TRUE;
+ return;
}
children = gtk_toolbar_list_children_in_focus_order (toolbar, dir);
}
g_list_free (children);
-
- return FALSE;
}
/* The focus handler for the toolbar. It called when the user presses
MNEMONIC_ACTIVATE,
GRAB_FOCUS,
FOCUS,
+ MOVE_FOCUS,
EVENT,
EVENT_AFTER,
BUTTON_PRESS_EVENT,
GdkEventFocus *event);
static gboolean gtk_widget_real_focus (GtkWidget *widget,
GtkDirectionType direction);
+static void gtk_widget_real_move_focus (GtkWidget *widget,
+ GtkDirectionType direction);
static gboolean gtk_widget_real_keynav_failed (GtkWidget *widget,
GtkDirectionType direction);
static PangoContext* gtk_widget_peek_pango_context (GtkWidget *widget);
_gtk_marshal_BOOLEAN__ENUM,
G_TYPE_BOOLEAN, 1,
GTK_TYPE_DIRECTION_TYPE);
+ widget_signals[MOVE_FOCUS] =
+ _gtk_binding_signal_new (I_("move_focus"),
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_CALLBACK (gtk_widget_real_move_focus),
+ NULL, NULL,
+ _gtk_marshal_VOID__ENUM,
+ G_TYPE_NONE,
+ 1,
+ GTK_TYPE_DIRECTION_TYPE);
widget_signals[EVENT] =
g_signal_new (I_("event"),
G_TYPE_FROM_CLASS (gobject_class),
return FALSE;
}
+static void
+gtk_widget_real_move_focus (GtkWidget *widget,
+ GtkDirectionType direction)
+{
+ GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
+
+ if (GTK_IS_WINDOW (toplevel) &&
+ GTK_WINDOW_GET_CLASS (toplevel)->move_focus)
+ {
+ GTK_WINDOW_GET_CLASS (toplevel)->move_focus (GTK_WINDOW (toplevel),
+ direction);
+ }
+}
+
static gboolean
gtk_widget_real_keynav_failed (GtkWidget *widget,
GtkDirectionType direction)
FRAME_EVENT,
ACTIVATE_FOCUS,
ACTIVATE_DEFAULT,
- MOVE_FOCUS,
KEYS_CHANGED,
LAST_SIGNAL
};
G_TYPE_NONE,
0);
- window_signals[MOVE_FOCUS] =
- g_signal_new (I_("move_focus"),
- G_TYPE_FROM_CLASS (gobject_class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (GtkWindowClass, move_focus),
- NULL, NULL,
- _gtk_marshal_VOID__ENUM,
- G_TYPE_NONE,
- 1,
- GTK_TYPE_DIRECTION_TYPE);
-
window_signals[KEYS_CHANGED] =
g_signal_new (I_("keys_changed"),
G_TYPE_FROM_CLASS (gobject_class),
void (* activate_focus) (GtkWindow *window);
void (* activate_default) (GtkWindow *window);
+
+ /* as of GTK+ 2.12 the "move-focus" signal has been moved to GtkWidget,
+ * so this is merley a virtual function now. Overriding it in subclasses
+ * continues to work though.
+ */
void (* move_focus) (GtkWindow *window,
GtkDirectionType direction);